/*!
 * DOMValidator 0.2, Copyright (c) 2008 Pelle Wessman, <http://kodfabrik.se/misc/domvalidator>, MIT Style License.
 */
/*extern DOMAssistant, $$ */
/**
 * @fileOverview A validator plugin for DOMAssistant
 * @name DOMValidator
 */

/** @scope DOMAssistant */
DOMAssistant.attach({
	/**
	 * Sets the rules for the validator
	 * @param  {object}       data            The properties for the validator
	 * @config {object}       [msg]           The element to which to add the error messages as new paragraphs. All paragraphs in this element will be removed. If none is specified the parent element of the validatorelement will be used.
	 * @config {string}       [tag]           The tag to use when adding a new error. Defaults to a p-tag
	 * @config {object}       [error]         The element to which to add a class if the validator element doesn't validate. If not specified the class will be added to the validator element.
	 * @config {object}       [valid]         The element to which to add a class if the validator element does validate. If not specified the class will be added to the validator element.
	 * @config {string|false} [classError]    CSS-class which is added when the element doesn't validate. Defaults to 'error'. Is disabled by setting it to a value of false.
	 * @config {string|false} [classValid]    CSS-class which is added when the element does validate. Defaults to 'valid'. Is disabled by setting it to a value of false.
	 * @config {string|false} [classErrorMsg] CSS-class to be added to the error messages. Defaults to 'errormsg'. Is disabled by setting it to a value of false.
	 * @config {string|true}  [req]           If set the value will be required. If set to a string that string will be added as an error message if it isn't specified.
	 * @config {string|true}  [email]         If set the value will need to in the format of an e-mail address. If set to a string that string will be added as an error message if it isn't formated as such.
	 * @config {object|array|string} [regexp] Either a Regular Expression object that the value of the element is tested against or a string containing the regular expression (without any / in the beginning and end). If an array then the first key should contain the regular expression as either an object or an string and the second key an error message to print if the test fails.
	 * @config {string|true}  [number]        If set the value will need to be an integer. If set to a string that string will be added as an error message if it isn't an integer.
	 * @config {int|array}    [min]           If an integer it defines the minimum number of characters that is allowed - or if the number property also is set the minimum value of that number. If an array the first field should be an integer defining the minimum and the second field should be a string containing an error message.
	 * @config {int|array}    [max]           If an integer it defines the maxmimum number of characters that is allowed - or if the number property also is set the maxmimum value of that number. If an array the first field should be an integer defining the maximum and the second field should be a string containing an error message.
	 * @config {function|array} [custom]      A function which will be called in the context of the validating element with the elements value asa parameter. Should return a true value if the element is valid and a false if it's invalid.  If an array then the first key should contain the function and the second key an error message to print if the test fails.
	 * @config {object|array} [equal]         If an object it should be a reference to another element which value should match the validator element's. If an array the first field should contain such a reference and the second field should contain an error message to be added if the values doesn't match.
	 * @config {function|object|array} [call] If a function it will be called once the element has been validated. If an object that object should be a reference to an element which validate-function will be called. If an array that array should contain a combination of functions and/or objects that are references to elements.
	 * @return {object}                       Returns the element itself to enable chaining
	 */
	setValidator: function(data){
		if (typeof data === 'object') {
			this.DOMValidator = data;
		}
		return this;
	},
	/**
	 * Validates the element according to the properties
	 * @param  {object}       [data]          The properties for the validator. If not specified the method will look for any properties set by the setValidator-method
	 * @config {object}       [msg]           The element to which to add the error messages as new paragraphs. All paragraphs in this element will be removed. If none is specified the parent element of the validatorelement will be used.
	 * @config {string}       [tag]           The tag to use when adding a new error. Defaults to a p-tag
	 * @config {object}       [error]         The element to which to add a class if the validator element doesn't validate. If not specified the class will be added to the validator element.
	 * @config {object}       [valid]         The element to which to add a class if the validator element does validate. If not specified the class will be added to the validator element.
	 * @config {string|false} [classError]    CSS-class which is added when the element doesn't validate. Defaults to 'error'. Is disabled by setting it to a value of false.
	 * @config {string|false} [classValid]    CSS-class which is added when the element does validate. Defaults to 'valid'. Is disabled by setting it to a value of false.
	 * @config {string|false} [classErrorMsg] CSS-class to be added to the error messages. Defaults to 'errormsg'. Is disabled by setting it to a value of false.
	 * @config {string|true}  [req]           If set the value will be required. If set to a string that string will be added as an error message if it isn't specified.
	 * @config {string|true}  [email]         If set the value will need to in the format of an e-mail address. If set to a string that string will be added as an error message if it isn't formated as such.
	 * @config {object|array|string} [regexp] Either a Regular Expression object that the value of the element is tested against or a string containing the regular expression (without any / in the beginning and end). If an array then the first key should contain the regular expression as either an object or an string and the second key an error message to print if the test fails.
	 * @config {string|true}  [number]        If set the value will need to be an integer. If set to a string that string will be added as an error message if it isn't an integer.
	 * @config {int|array}    [min]           If an integer it defines the minimum number of characters that is allowed - or if the number property also is set the minimum value of that number. If an array the first field should be an integer defining the minimum and the second field should be a string containing an error message.
	 * @config {int|array}    [max]           If an integer it defines the maxmimum number of characters that is allowed - or if the number property also is set the maxmimum value of that number. If an array the first field should be an integer defining the maximum and the second field should be a string containing an error message.
	 * @config {function|array} [custom]      A function which will be called in the context of the validating element with the elements value asa parameter. Should return a true value if the element is valid and a false if it's invalid.  If an array then the first key should contain the function and the second key an error message to print if the test fails.
	 * @config {object|array} [equal]         If an object it should be a reference to another element which value should match the validator element's. If an array the first field should contain such a reference and the second field should contain an error message to be added if the values doesn't match.
	 * @config {function|object|array} [call] If a function it will be called once the element has been validated. If an object that object should be a reference to an element which validate-function will be called. If an array that array should contain a combination of functions and/or objects that are references to elements.
	 * @return {boolean}                      Whether the element validates or not
	 */
	validate: function(data){
		var v = this.value, error = false, msg = [], length, i, errorElem, validElem, msgElem, vlength = v.length;

		if (data === undefined) {
			if (this.DOMValidator) {
				data = this.DOMValidator;
			} else {
				return true;
			}
		}
		if (v === undefined || typeof data !== 'object') {
			return false;
		}

		if (data.req !== undefined && vlength === 0) {
			error = true;
			if (typeof data.req === 'string') {
				msg[msg.length] = data.req;
			}
		} else if (vlength !== 0) {
			if (data.number !== undefined) {
				vlength = parseInt(v, 10);
				if (isNaN(vlength)) {
					error = true;
					if (typeof data.number === 'string') {
						msg[msg.length] = data.number;
					}
				}
			}

			if (typeof data.min === 'object' && typeof data.min[0] === 'number') {
				if (data.min[0] > vlength) {
					error = true;
					if (typeof data.min[1] === 'string') {
						msg[msg.length] = data.min[1];
					}
				}
			} else if (typeof data.min === 'number' && data.min > vlength) {
				error = true;
			}

			if (typeof data.max === 'object' && typeof data.max[0] === 'number') {
				if (data.max[0] < vlength) {
					error = true;
					if (typeof data.max[1] === 'string') {
						msg[msg.length] = data.max[1];
					}
				}
			} else if (typeof data.max === 'number' && data.max < vlength) {
				error = true;
			}

			if (data.email !== undefined) {
				var regexp = /\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/i;
				if (!regexp.test(v)) {
					error = true;
					if (typeof data.email === 'string') {
						msg[msg.length] = data.email;
					}
				}
			}

			if (typeof data.regexp === 'object') {
				if (data.regexp.constructor === Array) {
					var regexp = typeof data.regexp[0] === 'string' ? new RegExp(data.regexp[0]) : data.regexp[0];
					if (!regexp.test(v)) {
						error = true;
						if (typeof data.regexp[1] === 'string') {
							msg[msg.length] = data.regexp[1];
						}
					}
				} else if (!data.regexp.test(v)) {
					error = true;
				}
			} else if (typeof data.regexp === 'function' && !data.regexp.test(v)) {
				error = true;
			} else if (typeof data.regexp === 'string' && !(new RegExp(data.regexp)).test(v)) {
				error = true;
			}
		}

		if (typeof data.equal === 'object') {
			if (data.equal.constructor === Array) {
				if (typeof data.equal[0] === 'object' && data.equal[0].value !== undefined && v !== data.equal[0].value) {
					error = true;
					if (typeof data.equal[1] === 'string') {
						msg[msg.length] = data.equal[1];
					}
				}
			} else if (data.equal.value !== undefined && v !== data.equal.value) {
				error = true;
			}
		}

		if (typeof data.custom === 'object' && typeof data.custom[0] === 'function') {
			if (!data.custom[0].call(this, v)) {
				error = true;
				if (typeof data.custom[1] === 'string') {
					msg[msg.length] = data.custom[1];
				}
			}
		} else if (typeof data.custom === 'function' && !data.custom.call(this, v)) {
			error = true;
		}

		if (data.classErrorMsg === undefined) {
			data.classErrorMsg = 'errormsg';
		}
		if (data.classError === undefined) {
			data.classError = 'error';
		}
		if (data.classValid === undefined) {
			data.classValid = 'valid';
		}

		if (typeof data.classError === 'string') {
			if (typeof data.error !== 'object') {
				errorElem = this;
			} else {
				errorElem = data.error;
			}
			$$(errorElem).removeClass(data.classError);
		}
		if (typeof data.classValid === 'string') {
			if (typeof data.valid !== 'object') {
				validElem = this;
			} else {
				validElem = data.valid;
			}
			$$(validElem).removeClass(data.classValid);
		}

		if (typeof data.msg !== 'object') {
			msgElem = this.parentNode;
		} else {
			msgElem = data.msg;
		}
		if (typeof data.tag !== 'string') {
			data.tag = 'p';
		}
		$$(msgElem).elmsByTag(data.tag).remove();

		if (typeof data.call === 'function') {
			data.call();
		} else if (typeof data.call === 'object') {
			if (data.call.constructor === Array) {
				length = data.call.length;
				for (i = 0; i < length; i++) {
					if (typeof data.call[i] === 'function') {
						data.call[i]();
					} else {
						$$(data.call[i]).validate();
					}
				}
			} else {
				$$(data.call).validate();
			}
		}

		if (error) {
			if (typeof data.classError === 'string') {
				$$(errorElem).addClass(data.classError);
			}
			length = msg.length;
			for (i = 0; i < length; i++) {
				var p = $$(msgElem).create(data.tag, {}, true, msg[i]);
				if (typeof data.classErrorMsg === 'string') {
					p.addClass(data.classErrorMsg);
				}
			}
			return false;
		} else {
			if (typeof data.classValid === 'string') {
				$$(validElem).addClass(data.classValid);
			}
			return true;
		}
	}
});